package com.gc.cscott.cli;

import com.gc.cscott.domain.Competitor;
import com.gc.cscott.domain.contest.Contest;
import com.gc.cscott.domain.contest.Task;
import com.gc.cscott.domain.contest.TestCase;
import com.gc.cscott.domain.result.CompetitorResult;
import com.gc.cscott.domain.result.TestCaseResult;
import com.gc.cscott.domain.result.TestCaseResult.HowEnded;
import com.gc.cscott.fs.FileSystemWorker;
import com.gc.cscott.run.Chaser;
import com.gc.cscott.run.Executor;
import com.gc.cscott.store.ResultsStorage;
import com.gc.cscott.verify.OutputVerifier;
import com.gc.cscott.verify.OutputVerifier.OutputStatus;
import com.gc.cscott.verify.OutputVerifier.VerifiedOutput;
import com.gc.cscott.verify.OutputVerifierFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;

/**
 *
 * @author root
 */
public class ReVerifyTestReultsCommand implements Command {
    private static final Logger log = Logger.getLogger(ReVerifyTestReultsCommand.class.getSimpleName());
    @Inject
    Contest contest;
    @Inject
    FileSystemWorker fsWorker;
    @Inject
    OutputVerifierFactory outputVerifierFactory;
    @Inject
    ResultsStorage resultsStorage;

    public List<String> listHelp() {
        return ImmutableList.of(
                "Will re-validate solutions outputs not running existing test,",
                "but using stored output files.");
    }


    public List<String> perform(List<String> pendingArgs) {
        try {
            for (Competitor competitor : fsWorker.listCompetitorsAll()) {
                log.info("Competitor: "+competitor.getName());
                CompetitorResult results = resultsStorage.loadResults(competitor);
                for (Task task : contest.getTasksList()) {
                    log.info("  Task "+task.getNumber().intValue());
                    OutputVerifier outputVerifier = outputVerifierFactory.getInstance(task.getNumber());
                    for (final TestCase testCase : task.getTestCaseList()) {
                        log.info("    Test Case "+testCase.getNumber().intValue());
                        final TestCaseResult testCaseResult = results.getResult(testCase);
                        if (testCaseResult == null) {
                            log.info("      NO RESULTS STORED");
                        } else {
                            if (!ImmutableSet.of(HowEnded.Correct, HowEnded.Wrong, HowEnded.NotVerified).contains(testCaseResult.getResult())) {
                                log.info("      ignore status: "+testCaseResult.getResult());
                            } else {
                                File inputFile = fsWorker.getTestCaseInputSourceFile(testCase);
                                String inputText;
                                try {
                                    inputText = FileUtils.readFileToString(inputFile, "ASCII");
                                } catch (IOException ex) {
                                    throw new Error("Failed to load validation file", ex);
                                }

                                File validationFile = fsWorker.getTestCaseValidationFile(testCase);
                                String validationText;
                                try {
                                    validationText = FileUtils.readFileToString(validationFile, "ASCII");
                                } catch (IOException ex) {
                                    throw new Error("Failed to load validation file", ex);
                                }
                                String outputText = fsWorker.loadOutputFileFromResults(competitor, testCase);
                                if (outputText.length() < 100) {
                                    log.info("--------------");
                                    log.info(validationText);
                                    log.info("--------------");
                                    log.info(outputText);
                                }

                                final VerifiedOutput verifiedOutput = outputVerifier.verifyOuput(
                                        task.getNumber(),
                                        inputText,
                                        outputText,
                                        validationText);
                                if (verifiedOutput.getOutputStatus().getHowEnded()==testCaseResult.getResult()){
                                    log.info("        "+competitor.getName() +
                                        ": T" + task.getNumber().intValue() +
                                        " t" + testCase.getNumber().intValue() + " --> SAME " +
                                        verifiedOutput.getOutputStatus());
                                } else {
                                    storeResult(competitor, new TestCaseResult() {{
                                            setHint(verifiedOutput.getDetails());
                                            setPayedTime(testCaseResult.getPayedTime());
                                            if (verifiedOutput.getOutputStatus()==OutputStatus.Wrong){
                                                setResult(HowEnded.Wrong);
                                            } else {
                                                setResult(HowEnded.Correct);
                                            }
                                            setTestCase(testCase);
                                            setOutputDumpName(testCaseResult.getOutputDumpName());
                                        }
                                    }, ResultsStorage.WriteMode.Owerwrite);

                                    System.out.println("        "+competitor.getName() +
                                            ": T" + task.getNumber().intValue() +
                                            " t" + testCase.getNumber().intValue() + " # "+
                                            testCaseResult.getResult()+
                                            " --> " +
                                            verifiedOutput.getOutputStatus());
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new Error(e);
        }
        return pendingArgs;
    }

    private void storeResult(Competitor competitor, TestCaseResult testCaseResult, ResultsStorage.WriteMode writeMode) {
        try {
            resultsStorage.storeResult(competitor, testCaseResult,writeMode);
        } catch (IOException ex) {
            throw new Error("Failed to store results", ex);
        }
    }
}
